package com.bestcem.xm.qdes.grpc.service;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.bestcem.xm.qdes.controller.param.ProjectFinishParam;
import com.bestcem.xm.qdes.entity.QdesProject;
import com.bestcem.xm.qdes.entity.QdesQstruct;
import com.bestcem.xm.qdes.entity.QdesQuestion;
import com.bestcem.xm.qdes.grpc.v1.services.*;
import com.bestcem.xm.qdes.service.QdesProjectService;
import com.bestcem.xm.qdes.service.QdesQstructService;
import com.bestcem.xm.qdes.service.QdesQuestionService;
import io.grpc.stub.StreamObserver;
import lombok.*;
import lombok.extern.slf4j.Slf4j;
import net.devh.boot.grpc.server.service.GrpcService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.ObjectUtils;

import java.util.*;
import java.util.stream.Collectors;

import static com.bestcem.xm.qdes.grpc.v1.services.ProjectStatus.PROJECT_STATUS_ACTIVATED_VALUE;

/**
 * 获取问卷信息服务相关GRPC接口
 *
 * @author xiaoshu.shao <xiaoshu.shao@idiaoyan.com>
 * @version V1.0
 * @date 2022/9/21
 */
@GrpcService
@Slf4j
@RequiredArgsConstructor
public class BpProjectGrpcService extends BpProjectServiceGrpc.BpProjectServiceImplBase {

    private @NonNull QdesProjectService projectService;
    private @NonNull QdesQstructService qstructServicee;
    private @NonNull QdesQuestionService questionService;

    /**
     * 查询项目名称
     */
    @Override
    public void getProjectTitle(GetProjectTitleRequest request, StreamObserver<GetProjectTitleResponse> responseObserver) {
        QdesProject project = projectService.getById(request.getId());
        GetProjectTitleResponse.Builder builder = GetProjectTitleResponse.newBuilder();
        if (Objects.nonNull(project)) {
            builder.setTitle(project.getTitle());
            builder.setStatus(GrpcConstant.SUCCESS_STATUS);
        } else {
            builder.setStatus(GrpcConstant.FAIL_STATUS);
        }
        responseObserver.onNext(builder.build());
        responseObserver.onCompleted();
    }

    @Override
    public void getReleasedProject(GetReleasedProjectRequest request, StreamObserver<GetReleasedProjectResponse> responseObserver) {
        GetReleasedProjectResponse.Builder builder = GetReleasedProjectResponse.newBuilder();
        try {
            QdesProject project = projectService.getReleasedProject(request.getProjectId(), request.getVersion());
            if (Objects.nonNull(project)) {
                builder.setStatus(GrpcConstant.SUCCESS_STATUS);
                builder.setProject(toGrpcQdesProject(project));
            } else {
                builder.setStatus(GrpcConstant.FAIL_STATUS);
            }
            responseObserver.onNext(builder.build());
            responseObserver.onCompleted();
        } catch (Exception e) {
            log.error("[qdes] getReleasedProject error");
            builder.setStatus(GrpcConstant.FAIL_STATUS);
            responseObserver.onNext(builder.build());
            responseObserver.onCompleted();
        }
    }

    @Override
    public void getCurrentProject(GetCurrentProjectRequest request, StreamObserver<GetCurrentProjectResponse> responseObserver) {
        GetCurrentProjectResponse.Builder builder = GetCurrentProjectResponse.newBuilder();
        try {
            QdesProject project = projectService.getCurrentProject(request.getProjectId());
            if (Objects.nonNull(project)) {
                builder.setStatus(GrpcConstant.SUCCESS_STATUS);
                builder.setProject(toGrpcQdesProject(project));
            } else {
                builder.setStatus(GrpcConstant.FAIL_STATUS);
            }
            responseObserver.onNext(builder.build());
            responseObserver.onCompleted();
        } catch (Exception e) {
            log.error("[qdes] getCurrentProject error, e = ", e);
            builder.setStatus(GrpcConstant.FAIL_STATUS);
            responseObserver.onNext(builder.build());
            responseObserver.onCompleted();
        }
    }

    /**
     * 获取已发布答卷的问题
     */
    @Override
    public void getReleasedProjectQuestions(GetReleasedProjectQuestionsRequest request, StreamObserver<GetReleasedProjectQuestionsResponse> responseObserver) {
        String pid = request.getPid();
        int version = request.getVersion();
        boolean surveyOrder = request.getSurveyOrder();

        List<QuestionInfoList> infoList = new ArrayList<>();
        List<JSONObject> questions = getReleasedProjQuestions(pid, version, surveyOrder);
        for (JSONObject question : questions) {
            QuestionInfoList.Builder builder = QuestionInfoList.newBuilder();
            builder.setType(question.getString("qtype"));
            builder.setGid(question.getIntValue("gid"));
            builder.setTitle(question.getString("title"));

            JSONArray items = question.getJSONArray("items");
            if (items != null && !items.isEmpty()) {
                List<QuestionOption> opList = new ArrayList<>();
                items.forEach(it -> {
                    JSONObject item = (JSONObject) it;
                    QuestionOption.Builder optionBuilder = QuestionOption.newBuilder();
                    optionBuilder.setGid(item.getIntValue("gid"));
                    optionBuilder.setTitle(item.getString("title"));
                    opList.add(optionBuilder.build());
                });
                builder.addAllItems(opList);
            }
            JSONArray optionList = question.getJSONArray("option_list");
            if (optionList != null) {
                List<CascaderOption> casList = new ArrayList<>();
                if (items != null && !items.isEmpty()) {
                    items.forEach(it -> {
                        JSONObject item = (JSONObject) it;
                        CascaderOption.Builder casBuilder = CascaderOption.newBuilder();
                        casBuilder.setGid(item.getIntValue("gid"));
                        casBuilder.setTitle(item.getString("title"));
                        casBuilder.setPid(item.getString("pid"));
                        casBuilder.setOoid(item.getString("ooid"));
                        casList.add(casBuilder.build());
                    });
                }
                builder.addAllOptionList(casList);
            }
            JSONObject currentTemplate = question.getJSONObject("current_template");
            String current_template = !ObjectUtils.isEmpty(currentTemplate) ? currentTemplate.toJSONString() : "{}";
            //新版评价题current_template
            builder.setCurrentTemplate(current_template);
            if (!ObjectUtils.isEmpty(currentTemplate)) {
                List<String> tagList = new ArrayList<>();
                JSONArray scoreList = currentTemplate.getJSONArray("scoreList");
                if (scoreList != null) {
                    scoreList.forEach(sItem -> {
                        JSONObject scoreItem = (JSONObject) sItem;
                        tagList.add(scoreItem.getOrDefault("content", "").toString());
                    });
                    builder.addAllTagList(tagList);
                }
            }
            builder.setCustomQtype(question.getOrDefault("custom_qtype", "").toString());
            builder.setCid(question.getOrDefault("cid", "").toString());
            infoList.add(builder.build());
        }
        GetReleasedProjectQuestionsResponse.Builder builder = GetReleasedProjectQuestionsResponse.newBuilder();
        builder.addAllQuestions(infoList);
        builder.setStatus(GrpcConstant.SUCCESS_STATUS);
        responseObserver.onNext(builder.build());
        responseObserver.onCompleted();
    }

    private List<JSONObject> getReleasedProjQuestions(String pid, int version, boolean surveyOrder) {
        // 获取当前发布的问卷题目列表
        List<JSONObject> questions = new ArrayList<>();
        QdesProject project = projectService.getById(pid);
        if (project == null) {
            return questions;
        }
        if (version == 0) {
            if (project.getReleaseVer() == null) {
                return questions;
            }
            version = project.getReleaseVer();
        }
        return getReleaseProjQuestions(project, version, surveyOrder);
    }

    private List<JSONObject> getReleaseProjQuestions(QdesProject project, int version, boolean surveyOrder) {
        LambdaQueryWrapper<QdesQstruct> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(QdesQstruct::getProjectId, project.getId())
                .eq(QdesQstruct::getVersion, version).last("limit 1");
        QdesQstruct qstruct = qstructServicee.getOne(queryWrapper);
        if (qstruct == null) {
            return new ArrayList<>();
        }
        LambdaQueryWrapper<QdesQuestion> questionWrapper = Wrappers.lambdaQuery();
        questionWrapper.eq(QdesQuestion::getStructId, qstruct.getId());
        List<QdesQuestion> cursor = questionService.list(questionWrapper);
        projectService.fillQstructData(qstruct, cursor);
        Map<String, JSONObject> qdict = new HashMap<>();
        List<JSONObject> questions = new ArrayList<>();

        cursor.forEach(item -> {
            JSONObject qData = JSONObject.parseObject(item.getData());
            qData = qData == null ? new JSONObject() : qData;
            String qType = qData.getString("qtype");

            if (!qData.containsKey("custom_qtype")) {
                qData.put("custom_qtype", qType);
            }
            String customQType = qData.getString("custom_qtype");

            if ("score".equals(qType)) {
                if ("star".equals(customQType)) {
                    qData.put("custom_qtype", "star_score");
                }
                if ("cursor".equals(customQType)) {
                    qData.put("custom_qtype", "cursor_score");
                }
            }
            qdict.put(item.getGid().toString(), qData);
        });
        if (surveyOrder) {
            JSONObject dataJs = JSONObject.parseObject(qstruct.getData());
            for (JSONObject item : projectService.walkQstructQuestions(dataJs)) {
                JSONObject q = qdict.get(item.getString("gid"));
                if (q != null) {
                    item.keySet().forEach(key -> q.put(key, item.get(key)));
                    String cid = q.getString("cid");
                    cid = cid == null ? "" : cid;
                    q.put("_index", cid);
                    q.put("cid", cid);
                    questions.add(q);
                } else if (StringUtils.isNotEmpty(item.getString("qtype"))) {
                    String index = item.getString("_index");
                    if (StringUtils.isNotEmpty(index)) {
                        item.put("cid", index);
                    }
                    questions.add(item);
                }
            }
        } else {
            questions = new ArrayList<>(qdict.values());
            questions.forEach(q -> {
                String cid = q.getString("cid");
                cid = cid == null ? "" : cid;
                q.put("_index", cid);
                q.put("cid", cid);
            });
        }
        return questions;
    }

    private com.bestcem.xm.qdes.grpc.v1.services.QdesProject toGrpcQdesProject(QdesProject project) {
        if (project == null) {
            return null;
        }

        com.bestcem.xm.qdes.grpc.v1.services.QdesProject.Builder qdesProject = com.bestcem.xm.qdes.grpc.v1.services.QdesProject.newBuilder();

        if (project.getId() != null) {
            qdesProject.setId(project.getId());
        }
        if (project.getCode() != null) {
            qdesProject.setCode(project.getCode());
        }
        if (project.getTitle() != null) {
            qdesProject.setTitle(project.getTitle());
        }
        if (project.getCompanyId() != null) {
            qdesProject.setCompanyId(project.getCompanyId());
        }
        if (project.getVersion() != null) {
            qdesProject.setVersion(project.getVersion());
        }
        if (project.getReleaseVer() != null) {
            qdesProject.setReleaseVer(project.getReleaseVer());
        }
        if (project.getStructId() != null) {
            qdesProject.setStructId(project.getStructId());
        }
        if (project.getLogicId() != null) {
            qdesProject.setLogicId(project.getLogicId());
        }
        if (project.getCustomAttr() != null) {
            qdesProject.setCustomAttr(project.getCustomAttr());
        }
        if (project.getSource() != null) {
            qdesProject.setSource(project.getSource());
        }
        if (project.getExtId() != null) {
            qdesProject.setExtId(project.getExtId());
        }
        if (project.getTimerExpire() != null) {
            qdesProject.setTimerExpire(project.getTimerExpire());
        }
        if (project.getIsCopyDemo() != null) {
            qdesProject.setIsCopyDemo(project.getIsCopyDemo());
        }
        qdesProject.setStatus(project.getStatus());
        qdesProject.setPtype(project.getPtype());
        qdesProject.setQstruct(project.getQstruct());
        return qdesProject.build();
    }

    @Override
    public void finishProject(FinishProjectRequest request, StreamObserver<FinishProjectResponse> responseObserver) {
        FinishProjectResponse.Builder builder = FinishProjectResponse.newBuilder();
        try {
            ProjectFinishParam param = new ProjectFinishParam();
            param.setProjectId(request.getProjectId());
            projectService.finishProject(param);
            builder.setStatus(GrpcConstant.SUCCESS_STATUS);
            responseObserver.onNext(builder.build());
            responseObserver.onCompleted();
        } catch (Exception e) {
            log.error("[qdes] finishProject error");
            builder.setStatus(GrpcConstant.FAIL_STATUS);
            responseObserver.onNext(builder.build());
            responseObserver.onCompleted();
        }
    }

    @Override
    public void getQlistWithProjectId(GetQlistWithProjectIdRequest request, StreamObserver<GetQlistWithProjectIdResponse> responseObserver) {
        GetQlistWithProjectIdResponse.Builder builder = GetQlistWithProjectIdResponse.newBuilder();
        try {
            String pid = request.getProjectId();
            boolean is_preview = request.getIsPreview();
            QdesProject proj = projectService.getCurrentProject(pid);
            if (!is_preview && proj.getStatus().equals(PROJECT_STATUS_ACTIVATED_VALUE)) {
                proj = projectService.getReleasedProject(pid, null);
            }

            JSONObject data = Optional.ofNullable(JSONObject.parseObject(proj.getQstruct()).getJSONObject("data")).orElse(new JSONObject());
            List<Integer> all_qgids = new ArrayList<>();
            for (JSONObject q : projectService.walkQstructQuestions(data)) {
                all_qgids.add(q.getInteger("gid"));
            }
            Boolean breakExamOrder = Optional.ofNullable(JSONObject.parseObject(proj.getCustomAttr()).getBoolean("breakExamOrder")).orElse(Boolean.FALSE);
            List<Integer> gnt_list = new ArrayList<>();
            JSONArray jsonArrayrules = Optional.ofNullable(JSONObject.parseObject(proj.getCustomAttr()).getJSONArray("rules")).orElse(new JSONArray());
            if (!jsonArrayrules.isEmpty()) {
                List<RuleDto> ruleList = new ArrayList<>();
                for (Object it : jsonArrayrules) {
                    JSONObject item = (JSONObject) it;
                    RuleDto one = item.toJavaObject(RuleDto.class);
                    Integer k = all_qgids.contains(item.getInteger("startQ")) ? all_qgids.indexOf(item.getInteger("startQ")) : 0;
                    one.setIndex(k);
                    ruleList.add(one);
                }
                List<Integer> qList = new ArrayList<>();
                List<RuleDto> rules = ruleList.stream().sorted(Comparator.comparing(RuleDto::getIndex)).collect(Collectors.toList());
                for (RuleDto rule : rules) {
                    if (ObjectUtil.hasEmpty(rule.getEndQ(), rule.getCount(), rule.getStartQ())) {
                        continue;
                    }
                    int endQ = rule.getEndQ();
                    int count = rule.getCount();
                    int startQ = rule.getStartQ();
                    if (all_qgids.contains(startQ) && all_qgids.contains(endQ)) {
                        qList = all_qgids.subList(all_qgids.indexOf(startQ), all_qgids.indexOf(endQ) + 1);
                    }
                    if (count <= 0 || qList.size() == 0) {

                    } else if (count >= qList.size()) {
                        if (breakExamOrder) {
                            Collections.shuffle(qList);
                        }
                        gnt_list.addAll(qList);
                    } else {
                        List<Integer> list = RandomUtil.randomEleList(qList, count);
                        if (!breakExamOrder) {
                            Collections.sort(list);
                        }
                        gnt_list.addAll(list);
                    }
                }
            }
            builder.setStatus(GrpcConstant.SUCCESS_STATUS);
            builder.addAllQIds(gnt_list);
            responseObserver.onNext(builder.build());
            responseObserver.onCompleted();
        } catch (Exception e) {
            log.error("[qdes] getQlistWithProjectId error");
            builder.setStatus(GrpcConstant.FAIL_STATUS);
            responseObserver.onNext(builder.build());
            responseObserver.onCompleted();
        }
    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class RuleDto {
        Integer index;
        Integer endQ;
        Integer count;
        Integer startQ;
    }

}
